/*
* Copyright (c) 2025 Huawei Device Co., Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including but not limited to the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to provide persons with access to the Software
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGE OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

import { BreakpointManager } from '../../hooks/breakpoint';
import { getbreakpointManager } from '../../types/breakpoint';
import { AttributeManager } from '../../hooks/attributeManager';
import { EventManager } from '../../hooks/eventManager';
import { defaultConfig } from './default';
import { CallbackFunc } from '../../types/eventManager';

interface GridState {
  gridColumnStart: number,
  gridColumnEnd: number,
  gridRowStart: number,
  gridRowEnd: number,
}

/**
 * @description 基于web components实现栅格组件父标签
 */
export class GridRowComponent extends HTMLElement {
  private _property: string[];
  private _elementId: string;
  private _styleElement: HTMLStyleElement | null;
  private _unsubscribeBreakpoint: () => void;
  private _attributeManager: AttributeManager;
  private _eventManager: EventManager;
  private _breakpointManager: BreakpointManager | null;
  private _observe: MutationObserver | null;
  private _customEvent: CustomEvent | null;
  private _mountedCustomEvent: CustomEvent | null;

  constructor() {
    super();
    this._styleElement = null;
    this._property = ['scope', 'columns', 'gutterX', 'gutterY', 'direction', 'breakpointValue', 'breakpointReference'];
    this._elementId = '';
    this._attributeManager = new AttributeManager();
    this._eventManager = new EventManager(this);
    this._breakpointManager = null;
    this._unsubscribeBreakpoint = (): void => {};
    this._observe = null;
    this._customEvent = null;
    this._mountedCustomEvent = null;
  }

  connectedCallback(): void {
    this._observe = new MutationObserver((mutations) => {
      let count = 0;
      mutations.forEach(item => {
        if (count === 0) {
          count ++;
          this._attachStyles();
        }
      });
    });
    if (this.children && this.children.length) {
      const children = Array.from(this.children) as HTMLElement[];
      children.forEach(element => {
        this._observe?.observe(element, {attributes: true});
      });
    }

    this._customEvent = new CustomEvent('calculatePosition', {
      detail: {message: true},
      bubbles: true,
      composed: true,
    });

    this._mountedCustomEvent = new CustomEvent('gridRowMounted', {
      detail: {message: true},
      bubbles: true,
      composed: true,
    });
    this.dispatchEvent(this._mountedCustomEvent);
  }

  disconnectedCallback(): void {
    this._unsubscribeBreakpoint();
  }

  static get observedAttributes(): string[] {
    return ['scope', 'columns', 'gutterX', 'gutterY', 'direction', 'breakpointValue', 'breakpointReference'];
  }

  attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {
    if (this._property.includes(name) && oldValue !== newValue) {
      this._attachStyles();
    }
  }

  private _attachStyles(): void {
    if (this._styleElement) {
      this._styleElement.remove();
    }

    const scopeId = this.getAttribute('scope') || '';
    if (scopeId && !this._breakpointManager) {
      this._elementId = scopeId;
      const breakpointValue = this.getAttribute('breakpointValue') || defaultConfig.container.breakpointValue;
      let rowBreakpoints = null;
      if (breakpointValue && typeof breakpointValue === 'string') {
        rowBreakpoints = JSON.parse(breakpointValue);
      }
      const breakpointReference = this.getAttribute('breakpointReference') || defaultConfig.container.breakpointReference;

      this._breakpointManager = getbreakpointManager(breakpointReference, rowBreakpoints, scopeId);
      this._unsubscribeBreakpoint = this._breakpointManager.subscribeToBreakpoint(() => {
        this._attachStyles();
      });
    }

    const columns = this.getAttribute('columns') || defaultConfig.container.columns;
    const gutterX = this._attributeManager.processSize(this.getAttribute('gutterX'), defaultConfig.container.gutterX);
    const gutterY = this._attributeManager.processSize(this.getAttribute('gutterY'), defaultConfig.container.gutterY);
    const direction = this._attributeManager.processMultiValues(this.getAttribute('direction'), ['row', 'row-reverse'], [], defaultConfig.container.direction);
    
    const style = document.createElement('style') as HTMLStyleElement;
    style.textContent = `
      .${scopeId} {
        display: grid;
        grid-template-columns: repeat(${columns}, 1fr);
        grid-auto-rows: auto;
        gap: ${gutterY} ${gutterX};
        direction: ${direction === 'row' ? 'ltr' : 'rtl'};
        width: 100%;
        height: 100%;
      }
    `;
    this._styleElement = style;
    document.head.appendChild(style);

    // 计算按order排序后各组件的位置
    if (this._elementId) {
      requestAnimationFrame(() => {
        this.calChildElementPositions(columns);
      });
      if (this._customEvent) {
        this.dispatchEvent(this._customEvent);
      }
    }
  }

  private _getValidSpan(children: HTMLElement, totalColumns: number): number {
    const span = parseInt(children.getAttribute('span') ?? '1');
    if (span > totalColumns) {
      return totalColumns;
    }
    return span < 0 ? 1 : span;
  }

  private _getValidOffset(children: HTMLElement): number {
    const offset = parseInt(children.getAttribute('offset') ?? '0');
    return Math.max(offset, 0);
  }

  private _getFirstChildColumnStart(span: number, offset: number, totalColumns: number): number {
    if (span + offset > totalColumns) {
      return 1;
    } else {
      return offset + 1;
    }
  }
  
  private _getNonFirstChildColumnStart(gridState: GridState, span: number, offset: number, remainingWidth: number, totalColumns: number): void {
    if (offset === 0) {
      if (remainingWidth >= span) { // 表示该组件和前一个组件在同一行布局
        gridState.gridColumnStart = gridState.gridColumnEnd;
      } else {
        gridState.gridColumnStart = 1;
        gridState.gridRowStart += 1;
      }
      return;
    }

    // 表示该组件和前一个组件在同一行布局
    if (remainingWidth >= offset + span) {
      gridState.gridColumnStart = gridState.gridColumnEnd + offset;
      return;
    }

    // 表示该组件和前一个组件在不同行布局
    if (offset < remainingWidth) { // 表示该组件的偏移量小于上一行遗留的位置，因此该组件会在该行起始位置开始布局
      gridState.gridColumnStart = 1;
      gridState.gridRowStart = gridState.gridRowStart + 1;
      return;
    }

    while (totalColumns < offset - remainingWidth) {
      offset -= totalColumns;
    }
    if (totalColumns - (offset - remainingWidth) >= span) {
      gridState.gridColumnStart = offset - remainingWidth + 1;
      gridState.gridRowStart += 1;
    } else {
      gridState.gridColumnStart = 1;
      gridState.gridRowStart += 1;
    }
  }

  private _setChildElementProperty(sortedChildren: HTMLElement[], columns: string | number): void {
    const totalColumns = Number(columns);
    let remainingWidth = totalColumns;
    const gridState = {
      gridColumnStart: 1,
      gridColumnEnd: 1,
      gridRowStart: 1,
      gridRowEnd: 1,
    };
    sortedChildren.forEach((children, index) => {
      let span = this._getValidSpan(children, totalColumns);
      let offset = this._getValidOffset(children);
      if (index === 0) {
        gridState.gridColumnStart = this._getFirstChildColumnStart(span, offset, totalColumns);
      } else {
        this._getNonFirstChildColumnStart(gridState, span, offset, remainingWidth, totalColumns);
      }
      gridState.gridRowEnd = gridState.gridRowStart + 1;
      gridState.gridColumnEnd = gridState.gridColumnStart + span;
      remainingWidth = totalColumns - gridState.gridColumnEnd + 1;
      children.style.setProperty('--gridColumnStart', gridState.gridColumnStart + '');
      children.style.setProperty('--gridColumnEnd', gridState.gridColumnEnd + '');
      children.style.setProperty('--gridRowStart', gridState.gridRowStart + '');
      children.style.setProperty('--gridRowEnd', gridState.gridRowEnd + '');
    });
  }

  calChildElementPositions(columns: string | number): void {
    const parentElement = document.querySelector(`.${this._elementId}`);
    if (parentElement) {
      // 1. 获取所有子元素
      const children = Array.from(parentElement.children) as HTMLElement[];
      // 2. 按 order 属性排序
      const sortedChildren = children.sort((a, b) => {
        let orderA = parseInt(a.getAttribute('order') ?? '0') || 0;
        let orderB = parseInt(b.getAttribute('order') ?? '0') || 0;
        orderA = orderA < 0 ? 0 : orderA;
        orderB = orderB < 0 ? 0 : orderB;
        return orderA - orderB;
      });
      // 3. 计算元素位置
      this._setChildElementProperty(sortedChildren, columns);
    }
  }

  registerEvents(callback: CallbackFunc): void {
    if (this._elementId) {
      const gridInstance = document.querySelectorAll(`*[scope=${this._elementId}]`);
      const gridItems = gridInstance[0].querySelectorAll('multi-grid-col');
      if (gridItems.length) {
        gridItems.forEach((item, index) => {
          item.addEventListener('click', (e) => {
            e.preventDefault();
            this._eventManager.emit('gridColClick', {
              index,
              name: item.getAttribute('name') || '',
            });
          });
        });
        this._eventManager.on('gridColClick', callback);
      }
    }
  }
}

if (!customElements.get('multi-grid-row')) {
  customElements.define('multi-grid-row', GridRowComponent);
}
